<?php
namespace Lavender\Dao;

use Lavender\Core;
use Lavender\Errno;
use Lavender\Dao\Exception;

/*********************************************
 * 
 * 注意： 这个接口需要优化，因为做不到兼容。
 * 暂时仅仅能满足车辆那边的分布式处理（按日期分表 如表名：2014-06-18）
 * 未优化之前，请谨慎使用
 * 优化之后，此注释将会删掉
 * 
 ********************************************/

/**
 * table access class
 *
 */
abstract class MongoTableDistributed
{
	/**
	 * database system
	 * @var string
	 */
	protected $driver = 'mongodb';

	/**
	 * database config name
	 * @var string
	 */
	protected $database = 'demo_database';

	/**
	 * table
	 * @var string
	 */
	protected $table = 'demo_table';

	/**
	 * distributed database count
	 * @var int
	 */
	protected $database_num = 1;

	/**
	 * distributed table count
	 * @var int
	 */
	protected $table_num = 1;

	protected $db_handle;

	/**
	 * all instances cache
	 * @var array
	 */
	private static $instances = array();

	public function __construct()
	{
		$conf = Core::get_config('db', $this->database);
		if (empty($conf)) {
			throw new Exception('mongo conf is empty');
		}

		$this->database_num = $conf['database_num'] ?: 1;
		$this->table_num = $conf['table_num'] ?: 1;
	}

	/**
	 * get instance singletion
	 *
	 * @return Table
	 */
	public static function instance()
	{
		$class = get_called_class();
		if (empty(self::$instances[$class])) {
			self::$instances[$class] = new $class();
		}

		return self::$instances[$class];
	}

	/**
	 * multipie get
	 * @param array $ids 要分库分表的整型id
	 * @param array $query  要查询的字段
	 * @param array $fields 返回的字段
	 * @param array $order  按哪些字段进行排序 (eg. array('id' => 1, 'name' => -1))
	 * @param int $offset
	 * @param int $length
	 *
	 * @return array
	 */
	public function get($ids, $query = array(), $fields = array(), $order = '', $offset = '', $length = '', $is_res_id = false, $table_alias = '')
	{
		if (!empty($ids) && !is_array($ids)) {
			throw new Exception('param error', Errno::PARAM_INVALID);
		}
		
		$items = array();
		$id_groups = $this->group_keys($ids);
		foreach ($id_groups as $db_id => $ids_groupby_table) {
			$db = Core::get_database($this->driver, $this->database, $db_id);
			if(!empty($table_alias)){
				$items_in_table = $db->get($table_alias, $query, $fields, $order, $offset, $length,false,false,true);
				$items = array_merge($items, $items_in_table);
			}else{
				throw new Exception('table is empty', Errno::PARAM_INVALID);
			}
		}

		return $items;
	}

	/**
	 * group keys by db & table for multipie get
	 *
	 * @param array $ids
	 *
	 * @return array
	 */
	protected function group_keys($ids)
	{
		$group = array();

		if ($ids === null) {
			return $group;
		}

		foreach ($ids as $id) {
			$db_idx = $this->get_database_id($id);
			if (!isset($group[$db_idx])) {
				$group[$db_idx] = array();
			}

			if (!isset($group[$db_idx])) {
				$group[$db_idx] = array();
			}

			$group[$db_idx][] = $id;
		}

		return $group;
	}


	/**
	 * multipie get
	 * @param array $ids 要分库分表的整型id
	 * @param array $query  要查询的字段
	 * @param array $fields 返回的字段
	 * @param array $order  按哪些字段进行排序 (eg. array('id' => 1, 'name' => -1))
	 * @param int $limit_length
	 * @param int $offset
	 * @param int $length
	 *
	 * @return array
	 */
	public function get_limit($ids, $query = array(), $fields = array(), $limit_length = '', $order = '', $offset = '', $length = '', $is_res_id = false)
	{
		if (!empty($ids) && !is_array($ids)) {
			throw new Exception('param error', Errno::PARAM_INVALID);
		}
		
		$items = array();
		$id_groups = $this->group_keys($ids);
		foreach ($id_groups as $db_id => $ids_groupby_table) {
			$db = Core::get_database($this->driver, $this->database, $db_id);
			foreach ($ids_groupby_table as $table => $_ids) {
				$items_in_table = $db->get_limit($table, $query, $fields, $limit_length, $order, $offset, $length);
				$items = array_merge($items, $items_in_table);
			}
		}

		return $items;
	}

	/**
	 * single record get
	 *
	 * @param int $id
	 * @param array $filter filter condition
	 * @param string $order
	 *
	 * @return mixed
	 */
	public function get_single($id, $query = array(), $fields = array())
	{
		if (is_null($id)) {
			throw new Exception('param error', Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->get_single($table, $query, $fields);
	}


	/**
	 * count
	 * @param array $query  要搜索的字段
	 * @param int $offset limit offset
	 * @param int $length limit size
	 *
	 * @return int
	 */
	public function count($id, $query = array(), $offset = null, $length = null)
	{
		if (is_null($id)) {
			throw new Exception('param error', Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->count($table, $query, $offset, $length);
	}

	/**
	 * insert records
	 * @param  array $content 插入的内容
	 * @param  array $options Options for the insert
	 */
	public function insert($id, $content, $options = array())
	{
		if (!is_array($content)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->insert($table, $content, $options);
	}

	/**
	 * batchinsert records
	 * @param  array $content 插入的内容
	 * @param  array $options Options for the insert
	 */
	public function batch_insert($id, $content, $options = array())
	{
		if (!is_array($content)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->batch_insert($table, $content, $options);
	}

	/**
	 * update the objects
	 * @param  array $criteria   更新的条件
	 * @param  array $new_object 更新的内容
	 * @param  array $options    更新的额外设置
	 */
	public function update($id, $criteria, $new_object, $options = array())
	{
		if (!is_array($criteria)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		if (!is_array($new_object)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->update($table, $criteria, $new_object, $options);
	}

	/**
	 * delete the objects
	 * @param  array $criteria   删除的条件
	 * @param  array $options    删除的额外设置
	 */
	public function delete($id, $criteria, $options = array())
	{
		if (!is_array($criteria)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->delete($table, $criteria, $options);
	}

	/**
	 * exec command
	 * @param  array $command   要执行的MongoDB操作
	 */
	public function command($id, $command)
	{
		if (!is_array($command)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);

		return $db->command($command);
	}

	/**
	 * calculate aggregated values
	 * @param  array $criteria   删除的条件
	 * @param  array $options    删除的额外设置
	 */
	public function aggregate($id, $pipeline)
	{
		if (!is_array($pipeline)) {
			throw new Exception("param must be array", Errno::PARAM_INVALID);
		}

		$id = intval($id);
		$db = $this->get_database_instance($id);
		$table = $this->get_table_name($id);

		return $db->aggregate($table, $pipeline);
	}

	protected function get_database_instance($id)
	{
		$db_id = $this->get_database_id($id);
		return Core::get_database($this->driver, $this->database, $db_id);
	}

	protected function get_database_id($id)
	{
		return $this->database_num > 1 ? $id % $this->database_num : null;
	}

	protected function get_table_name($route_id)
	{
		if ($this->table_num <= 1) {
			return $this->table;
		}

		return $this->table . (intval($route_id / $this->database_num) % $this->table_num);
	}

}